<?php
/*
# $Id$
*/

class BuanSourceFile {

	private $source = '';
	private $target = '';
	private $template = '';

	public function __construct($source) {
		$this->source = $source;
	}

	public function setTarget($target) {
		$this->target = $target;
	}

	public function setTemplate($template) {
		$this->template = $template;
	}

	public function parse(&$document) {

		// Determine if source file contains any class definition(s) and include it
		$document = '';
		$className = preg_replace("/\..+$/", "", basename($this->source));
		$regex = "class\s+".preg_quote($className, '/')."[^\{]+\{";
		$sourceCode = preg_replace("/(php)*\?\>$/ims", "", preg_replace("/^\<\?php/ims", "", file_get_contents($this->source)));
		if(!preg_match("/class\s+".preg_quote(preg_replace("/\..*$/", "", basename($this->source)), '/')."[^\{]+\{/ims", $sourceCode)) {
			return FALSE;
		}

		// Remove classes on which this class is dependant and eval source
		$dec = get_declared_classes();
		if(preg_match("/class ([^\s]+) (extends|implements) ([^\s]+)/ims", $sourceCode, $m)) {
			$sourceCode = preg_replace("/class {$m[1]} {$m[2]} {$m[3]}/ims", "class {$m[1]}", $sourceCode);
		}
		eval($sourceCode);
		$classes = array_diff(get_declared_classes(), $dec);

		// Parse class definitions
		$buffer = '';
		foreach($classes as $class) {
			$class = new ReflectionClass($class);
			$class->sourceCode = $sourceCode;

			// Class info
			$data = array();
			$data['class'] = new StdClass();
			$data['class']->name = $class->getName();
			$data['class']->dependancies = '';

			// Constants
			$data['constants'] = array();
			$constants = $class->getConstants();
			foreach($constants as $k=>$c) {
				if(preg_match("/\#\s*@constant\s([a-z0-9\s]+?)\s{$k}(.*?)\*\//ims", $class->sourceCode, $m)) {
					$constant = new StdClass();
					$constant->type = $m[1];
					$constant->name = $k;
					$constant->value = $c;
					$constant->scope = 'TODO';
					$constant->comment = str_replace("#", "", $m[2]);
					$data['constants'][] = $constant;
				}
			}

			// Properties
			$data['properties'] = array();
			$properties = $class->getProperties();
			foreach($properties as $prop) {
				$property = new StdClass();
				$property->name = $prop->getName();
				$property->scope = $prop->isProtected() ? 'protected' : ($prop->isPrivate() ? 'private' : 'public');
				$property->isStatic = $prop->isStatic();
				$property->type = NULL;
				$property->comment = NULL;
				if(preg_match("/\#\s*@property\s([a-z0-9\s]+?)\s\\\${$prop->getName()}(.*?)\*\//ims", $class->sourceCode, $m)) {
					$property->type = $m[1];
					$property->comment = preg_replace("/^[\t|\r|\n]+/ims", "", str_replace("#", "", $m[2]));
				}
				$data['properties'][] = $property;
			}

			// Methods
			$data['methods'] = array();
			$methods = $class->getMethods();
			foreach($methods as $meth) {
				$method = new StdClass();
				$method->name = $meth->getName();
				$method->isStatic = $meth->isStatic();
				$method->scope = $meth->isProtected() ? 'protected' : ($meth->isPrivate() ? 'private' : 'public');
				$method->type = NULL;
				$method->comment = NULL;
				$method->isDeprecated = FALSE;
				$method->parameterString = '';
				$method->parameters = array();

				if(preg_match("/\#\s*@method\s([a-z0-9\s\|]+?)\s{$meth->getName()}\s*\(([^\)]*)\)(.*?)\*\//ims", $class->sourceCode, $m)) {
					$method->type = $m[1];
					$method->isDeprecated = FALSE;
					$method->parameterString = isset($m[2]) ? $m[2] : '';

					// Extract comments
					$comments = explode("[[DIVIDE]]", preg_replace("/\#\s*[\r\n]+/ims", "[[DIVIDE]]", trim($m[3])));
					if(count($comments)==0) {
						$paramComments = $methodComments = '';
					}
					else if(count($comments)==1) {
						$paramComments = '';
						$methodComments = $comments[0];
					}
					else if(count($comments)>1) {
						$paramComments = array_shift($comments);
						$methodComments = implode("\n", $comments);

						$paramComments = preg_split("/\#\s*\\$/ims", $paramComments);
						array_shift($paramComments);
						foreach($paramComments as $k=>$pc) {
							$paramComments[$k] = preg_replace("/^[^=]+=\s*/ims", "", $pc);
						}
					}
					$method->comment = str_replace("#", "", $methodComments);

					// Extract parameters
					$params = $meth->getParameters();
					foreach($params as $k=>$param) {
						preg_match("/([^\s]+)\s\\\${$param->getName()}/ims", $m[2], $pm);
						$parameter = new StdClass();
						$parameter->type = isset($pm[1]) ? preg_replace("/[\[\]]/", "", $pm[1]) : '';
						$parameter->name = $param->getName();
						$parameter->isPassedByReference = $param->isPassedByReference();
						$parameter->comment = is_array($paramComments) ? (isset($paramComments[$k]) ? $paramComments[$k] : 'No comments.') : $paramComments;
						$method->parameters[] = $parameter;
					}
				}
				$data['methods'][] = $method;
			}

			// Render document
			$buffer .= $this->renderClass($data);
			$document .= $buffer;
		}

		$document = $this->renderWrapper($document);

		// Result
		return TRUE;
	}

	private function renderClass($data) {
		extract($data);
		ob_start();
		include($this->template.'/class.tpl');
		$buffer = ob_get_clean();
		return $buffer;
	}

	private function renderWrapper($content) {
		ob_start();
		include($this->template.'/wrapper.tpl');
		$buffer = ob_get_clean();
		return $buffer;
	}
}
?>